package org.budo.mybatis.dao.statement.builder.impl;

import java.util.Date;
import java.util.Map;

import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.session.Configuration;
import org.budo.mybatis.dao.entity.configer.MyBatisDaoEntityConfiger;
import org.budo.mybatis.dao.statement.builder.StatementBuilder;
import org.budo.mybatis.dao.statement.builder.StatementBuilderConsts;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author limingwei
 */
@Slf4j
@Getter
@Setter
public abstract class AbstractStatementBuilder implements StatementBuilder, StatementBuilderConsts {
    private MyBatisDaoEntityConfiger entityConfiger;

    /**
     * 最大级联深度
     */
    private Integer associationMaxDepth = 1;

    public String sqlStatementId(String sql) {
        if (null == sql || sql.trim().isEmpty()) {
            throw new IllegalArgumentException("#60 sql is null or empty, sql=" + sql);
        }

        return sql.replace(".", "POINT") //
                .replace("&", "AMP") //
                .replace(">", " GT ") //
                .replace("<", " LT ") //
                .replace("\"", "QM") // 引号 quotation marks
                .replace("/", "FS"); // 正斜杠 forward slash
    }

    public synchronized String assertExistsResultMapReturnAllId(Configuration configuration, Class<?> entityType) {
        ResultMapBuilder resultMapBuilder = new ResultMapBuilder(this.getEntityConfiger(), this.getAssociationMaxDepth());
        resultMapBuilder.getResultMapId_Basic(configuration, entityType);

        return resultMapBuilder.getResultMapId_All(configuration, entityType);
    }

    public String resultMapOrResultType(Configuration configuration, Class<?> entityType, String sql) {
        if (entityType.isPrimitive() || Number.class.isAssignableFrom(entityType) || Boolean.class.equals(entityType) || String.class.equals(entityType) || Date.class.equals(entityType)) {
            return " resultType=\"" + entityType.getName() + "\" ";
        }

        if (Map.class.isAssignableFrom(entityType)) {
            return " resultType=\"map\" ";
        }

        if (StringUtil.isEmpty(sql)) {
            return " resultMap=\"" + this.assertExistsResultMapReturnAllId(configuration, entityType) + "\" ";
        }

        return " resultMap=\"" + this.assertExistsResultMapReturnAllId(configuration, entityType) + "\" ";
    }

    protected String resultMap_type(Configuration configuration, Class<?> entityType, String sql) {
        ResultMapBuilder resultMapBuilder = new ResultMapBuilder(this.getEntityConfiger(), this.getAssociationMaxDepth());
        resultMapBuilder.setAnnotation(false);
        String resultMap = resultMapBuilder.getResultMapId_All(configuration, entityType);
        return " resultMap=\"" + resultMap + "\" ";
    }

    public synchronized Boolean hasStatement(Configuration configuration, String statementId) {
        try {
            return configuration.hasStatement(statementId);
        } catch (IncompleteElementException e) {
            if (("" + e).contains("Could not find result map ") && ("" + e).contains(".$ResultMap")) {
                String type = ("" + e).replace("org.apache.ibatis.builder.IncompleteElementException: Could not find result map ", "").replace(".$ResultMap$All", "");
                log.warn("hasStatement, statementId=" + statementId + ", 正在生成 " + type + ".$ResultMap");

                Class<?> entityType = ReflectUtil.classForName(type);
                this.resultMapOrResultType(configuration, entityType, null);

                return this.hasStatement(configuration, statementId);
            }

            throw new RuntimeException(e);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
}
